﻿//
// MainForm.cs: The main window of the application.
//
// Copyright 2010 Michael Goethe
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using S9PalCommon;
using S9PalCommon.ArtistLibrary;

namespace S9Pal
{
    /// <summary>
    /// The main window of the application.
    /// </summary>
    public partial class MainForm : Form
    {
        #region Fields

        private delegate void UpdateArtistNamesListBoxDelegate();

        private const int WM_DEVICECHANGE = 0x219;
        private const int DBT_DEVICEARRIVAL = 0x8000;
        private const int DBT_DEVICEREMOVECOMPLETE = 0x8004;

        private List<Syncer> syncers = new List<Syncer>();
        private ArtistLibraryUpdater artistLibraryUpdater;
        private bool libraryUpdaterRunning = false;

        #endregion

        #region Constructors

        public MainForm()
        {
            InitializeComponent();
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Loads all compatible plugins within the plugins folder.
        /// </summary>
        public void LoadPlugins()
        {
            // Call the find plugins routine, to search in our Plugins Folder
            Global.Plugins.FindPlugins(Application.StartupPath + @"\Plugins");

            foreach (AvailablePlugin plugin in Global.Plugins.AvailablePlugins)
            {
                // Load the main controls within the current plugin.
                foreach (UserControl uc in plugin.Instance.MainControls)
                {
                    Type ucType = uc.GetType();

                    // Enable this component by default.
                    if (!Global.Settings.PluginComponentEnabled.ContainsKey(ucType.ToString()))
                        Global.Settings.PluginComponentEnabled.Add(ucType.ToString(), true);

                    // Add the control to the main form.
                    if (Global.Settings.PluginComponentEnabled[ucType.ToString()] == true)
                    {
                        TabPage tp = new TabPage((uc as IMainControl).DisplayTitle);
                        artistTabControl.TabPages.Add(tp);
                        uc.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right;
                        uc.Size = tp.Size;
                        tp.Controls.Add(uc);
                    }
                }

                // Load the workers within the currrent plugin.
                for (int i = 0; i < plugin.Instance.Workers.Count; i++)
                {
                    Worker worker = plugin.Instance.Workers[i];
                    Type workerType = worker.GetType();

                    // Enable this component by default.
                    if (!Global.Settings.PluginComponentEnabled.ContainsKey(workerType.ToString()))
                        Global.Settings.PluginComponentEnabled.Add(workerType.ToString(), true);

                    // Add the worker to the drop down menu of workers.
                    if (Global.Settings.PluginComponentEnabled[workerType.ToString()] == true)
                    {
                        workersToolStripMenuItem.DropDownItems.Add(worker.Name, worker.Icon);
                        workersToolStripMenuItem.DropDownItems[i].Tag = worker;
                    }
                }

                // Load the syncers within the current plugin.
                for (int i = 0; i < plugin.Instance.Syncers.Count; i++)
                {
                    Syncer syncer = plugin.Instance.Syncers[i];
                    Type syncerType = syncer.GetType();

                    // Enable this component by default.
                    if (!Global.Settings.PluginComponentEnabled.ContainsKey(syncerType.ToString()))
                        Global.Settings.PluginComponentEnabled.Add(syncerType.ToString(), true);

                    // Add the syncer to the list of active syncers.
                    if (Global.Settings.PluginComponentEnabled[syncerType.ToString()] == true)
                        syncers.Add(syncer);
                }
            }
        }

        /// <summary>
        /// Clears all form references to plugin components.
        /// </summary>
        public void PurgePlugins()
        {
            artistTabControl.Controls.Clear();
            workersToolStripMenuItem.DropDownItems.Clear();
            syncers.Clear();
        }

        /// <summary>
        /// Checks if a device is found, and if so, updates controls accordingly.
        /// </summary>
        public void UpdateDeviceStatus()
        {
            Syncer.UpdateDrive();

            if (Syncer.Drive != null)
            {
                connectionPictureBox.Image = GetImageFromResources("deviceconnected.png");
                volumeNameLabel.Text = Syncer.Drive.VolumeLabel;
                connectionLabel.Text = "Connected.";
                syncToolStripMenuItem.Enabled = true;
                if (Global.Settings.PullLibraryFromDevice)
                    updateLibraryToolStripMenuItem.Enabled = true;
            }
            else
            {
                connectionPictureBox.Image = GetImageFromResources("devicenotdetected.png");
                volumeNameLabel.Text = "";
                connectionLabel.Text = "No device detected.";
                syncToolStripMenuItem.Enabled = false;
                if (Global.Settings.PullLibraryFromDevice)
                    updateLibraryToolStripMenuItem.Enabled = false;
            }

            if (!Global.Settings.PullLibraryFromDevice)
                updateLibraryToolStripMenuItem.Enabled = true;
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// Intercepts system messages.
        /// </summary>
        /// <param name="m">Reference to message.</param>
        protected override void WndProc(ref Message m)
        {
            // Trap WM_DEVICECHANGE 
            if (m.Msg == WM_DEVICECHANGE && (m.WParam.ToInt32() == DBT_DEVICEARRIVAL || m.WParam.ToInt32() == DBT_DEVICEREMOVECOMPLETE))
            {
                // A device has been inserted or removed (not necessarily the device we're looking for, just a device),
                // so call this method which will check if the device has been connected (or is still connected) and update accordingly.
                UpdateDeviceStatus();
            }
            base.WndProc(ref m);
        }

        #endregion

        #region Private Methods

        private Image GetImageFromResources(String fileName)
        {
            return new Bitmap(System.Reflection.Assembly.GetEntryAssembly().GetManifestResourceStream("S9Pal.Resources." + fileName));
        }

        private void UpdateArtistNamesListBox()
        {
            artistNamesListBox.Items.Clear();
            artistNamesListBox.Items.AddRange(S9PalCommon.Global.ArtistLibrary.GetArtistNames());
        }

        #endregion

        #region Events

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Show the about dialog.
            AboutForm aboutForm = new AboutForm();
            aboutForm.ShowDialog(this);
        }

        private void artistLibraryUpdater_StatusChanged(object sender, EventArgs e)
        {
            // When the library updater has finished, update the list box of artist names.
            ArtistLibraryUpdater artistLibraryUpdater = (ArtistLibraryUpdater)sender;
            if (artistLibraryUpdater.Status == WorkerStatus.Finished)
            {
                BeginInvoke(new UpdateArtistNamesListBoxDelegate(UpdateArtistNamesListBox));
                libraryUpdaterRunning = false;
            }
            else if (artistLibraryUpdater.Status == WorkerStatus.Stopped)
            {
                libraryUpdaterRunning = false;
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Close the application.
            this.Close();
        }

        private void lstArtist_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Update SelectedArtist with the currently selected artist in the list box.
            S9PalCommon.Global.SelectedArtist = S9PalCommon.Global.ArtistLibrary.Artists[artistNamesListBox.SelectedItem.ToString()];
        }

        private void mainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            // Save and clean-up.
            Global.Settings.SaveSettings();
            Global.Plugins.ClosePlugins();

            if (artistLibraryUpdater != null)
            {
                if (artistLibraryUpdater.Status == WorkerStatus.Active)
                {
                    artistLibraryUpdater.Stop();
                    while (artistLibraryUpdater.Status != WorkerStatus.Stopped)
                    {
                        Thread.Sleep(50);
                    }
                }
            }

            S9PalCommon.Global.ArtistLibrary.SaveLibrary();
        }

        private void mainForm_Load(object sender, EventArgs e)
        {
            // Check for a device and if one is found, update controls accordingly.
            UpdateDeviceStatus();

            // Load all the plugins in the plugins directory.
            LoadPlugins();

            // Get the window size and state of the previous instance of the application.
            this.WindowState = Global.Settings.WindowState;
            this.Size = Global.Settings.WindowSize;

            // Populate the list box of artist names.
            artistNamesListBox.Items.AddRange(S9PalCommon.Global.ArtistLibrary.GetArtistNames());
        }

        private void mainForm_Resize(object sender, EventArgs e)
        {
            // Update the saved window state.
            Global.Settings.WindowState = this.WindowState;
        }

        private void mainForm_ResizeEnd(object sender, EventArgs e)
        {
            // Update the saved window size.
            Global.Settings.WindowSize = new Size(this.Width, this.Height);
        }

        private void pluginsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Show the plugins dialog.
            PluginsForm pluginsForm = new PluginsForm();
            pluginsForm.ShowDialog(this);
        }

        private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Show the settings dialog.
            SettingsForm settingsForm = new SettingsForm();
            settingsForm.ShowDialog(this);
        }

        private void syncToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Perform sync using all available syncers.
            foreach (Syncer syncer in syncers)
            {
                JobViewerControl jvc = new JobViewerControl(syncer);
                jobManagerControl.AddJob(jvc);
                syncer.Start();
            }
        }

        private void updateLibraryToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (!libraryUpdaterRunning)
            {
                libraryUpdaterRunning = true;

                JobViewerControl jvc;

                // Update the library either from the device or from an explicit custom path.
                if (Global.Settings.PullLibraryFromDevice)
                    artistLibraryUpdater = new ArtistLibraryUpdater(S9PalCommon.Global.ArtistLibrary, Syncer.Drive.ToString());
                else
                    artistLibraryUpdater = new ArtistLibraryUpdater(S9PalCommon.Global.ArtistLibrary, Global.Settings.CustomLibraryDirectory.ToString());

                // Attach an event handler to catch status changes, like when the updater is finished.
                artistLibraryUpdater.StatusChanged += new EventHandler(artistLibraryUpdater_StatusChanged);

                // Add the updater to the job manager.
                jvc = new JobViewerControl(artistLibraryUpdater);
                jobManagerControl.AddJob(jvc);

                // Start the updater.
                artistLibraryUpdater.Start();
            }
            else if (artistLibraryUpdater.Status == WorkerStatus.Paused)
            {
                artistLibraryUpdater.Start();
            }
        }

        private void workersToolStripMenuItem_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            // Start the clicked worker.
            Worker worker = (Worker)e.ClickedItem.Tag;
            JobViewerControl jvc = new JobViewerControl(worker);

            jobManagerControl.AddJob(jvc);
            worker.Start();
        }

        #endregion
    }
}